<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue</title>
</head>

<body>
    <img src="./ndoeType.jpg" alt="">
    <div id="app">
        <input v-model="text" type="text">
        {{text}}
    </div>
    <script>
        class Vue {
            constructor(options) {
                this.$options = options
                let dom = document.querySelector(this.$options.el)
                new Observer(this.$options.data)
                const f = new Compile(dom, this.$options.data)
                dom.append(f)
                // console.log(this.$options.data);
            }
        }

        // 数据劫持
        class Observer {
            constructor(data) {
                this.walk(data)
            }
            walk(data) {
                Object.keys(data).forEach(key => {
                    this.defineReactive(data, key, data[key])
                })
            }
            // 劫持data数据
            defineReactive(data, key, value) {
                let dep = new Dep()
                Object.defineProperty(data, key, {
                    get() {
                        if (Dep.target) {
                            dep.addSub(Dep.target)
                        }
                        return value
                    },
                    set(newValue) {
                        value = newValue
                        dep.notify()
                    }
                })
            }
        }

        class Dep {
            constructor() {
                this.sub = []
            }
            addSub(watcher) {
                this.sub.push(watcher)
            }
            notify() {
                this.sub.forEach(watcher => watcher.updata())
            }
        }
        // 

        class Watcher {
            constructor(vm, node, name) {
                Dep.target = this
                this.vm = vm;
                this.node = node;
                this.name = name
                this.updata()
                Dep.target = null
            }
            get() {
                this.value = this.vm[this.name]
            }
            updata() {
                this.get()
                this.node.nodeValue = this.value
            }
        }



        class Compile {
            constructor(node, vm) {
                this.node = node;
                this.vm = vm;
                return this.nodeList(this.node)
            }
            parse(node, vm) {
                if (node.nodeType === 1) {
                    // node.attributes 获取dom 节点的 属性类数组
                    Array.from(node.attributes).forEach(v => {
                        // console.log(v); //=>
                        /* {baseURI: "http://127.0.0.1:5500/src/miniVue.html"
                            childNodes: NodeList []
                            firstChild: null
                            isConnected: false
                            lastChild: null
                            localName: "v-model"
                            name: "v-model"
                            namespaceURI: null
                            nextSibling: null
                            nodeName: "v-model"
                            nodeType: 2
                            nodeValue: "text"
                            ownerDocument: document
                            ownerElement: input
                            parentElement: null
                            parentNode: null
                            prefix: null
                            previousSibling: null
                            specified: true
                            textContent: "text"
                            value: "text"} */
                        if (v.nodeName === 'v-model') {
                            node.addEventListener('input', e => {
                                // 更新vm实例的值
                                vm[v.nodeValue] = e.target.value
                            })
                            // input 的初始值
                            node.value = vm[v.nodeValue]
                        }
                    })
                }
                // 使用正则截取插值表达式
                let reg = /\{\{(.*)\}\}/
                if (node.nodeType === 3) {
                    // 过滤掉空白
                    if (reg.test(node.nodeValue)) {
                        // RegExp.$1  获取正则匹配的第一个小括号里面的值
                        node.nodeValue = vm[RegExp.$1]

                        // 收集依赖
                        new Watcher(vm, node, RegExp.$1)
                    }
                }
            }
            nodeList(node) {
                // 创建文档碎片
                let f = document.createDocumentFragment()
                let child;
                while (child = node.firstChild) {
                    this.parse(child, this.vm)
                    f.append(child)
                }
                return f
            }
        }



        new Vue({
            el: '#app',
            data: {
                text: 'hellow Vue'
            }
        })
    </script>
</body>

</html>